package handler

import (
	"encoding/base64"
	"errors"
	"fmt"
	"gitee.com/simple-set/simple.io/src/event"
	"gitee.com/simple-set/simple.io/src/process/codec"
	"gitee.com/simple-set/simple.io/src/process/http"
	"gitee.com/simple-set/simple.proxy/src/config"
	"log/slog"
	stdlibHttp "net/http"
	"strings"
)

type HttpProxy struct {
	client         *Client
	auth           *AuthUser
	responseEncode *http.ResponseEncode
	interceptor    *Interceptor
}

// NewHttpProxy 构造函数
func NewHttpProxy() *HttpProxy {
	httpProxy := &HttpProxy{client: NewClientPlus(NewHttpBackend()), responseEncode: http.NewResponseEncode()}
	if config.Config.Http.AuthFilePath != "" {
		httpProxy.auth = NewAuthUser()
		httpProxy.auth.LoadUser(config.Config.Http.AuthFilePath)
	}
	if config.Config.Http.AuthUser != "" {
		if httpProxy.auth == nil {
			httpProxy.auth = NewAuthUser()
		}
		httpProxy.auth.AddUser(config.Config.Http.AuthUser, config.Config.Http.AuthPassword)
	}
	if config.Config.Http.AclFilePath != "" {
		httpProxy.interceptor = NewInterceptor(config.Config.Http.AclFilePath)
		httpProxy.client.SetInterceptor(httpProxy.interceptor)
	}
	return httpProxy
}

func (h *HttpProxy) Active(session *event.Session, value any) (any, error) {
	request, ok := value.(*http.Request)
	if !ok {
		return nil, fmt.Errorf("invalid request: %v", request)
	}

	if h.interceptor != nil {
		if h.httpACL(request) {
			slog.Warn(fmt.Sprintf("http proxy has been intercepted, %s -> %s", session.Socket.RemoteAddr(), request.Request.Host))
			request.Response.SetStatusCodeAndText(stdlibHttp.StatusForbidden, stdlibHttp.StatusText(stdlibHttp.StatusForbidden))
			return h.responseEncode.Encode(request.Response), nil
		}
	}

	// 如果代理服务器开启认证(设置了账号和密码), 则必须认证通过
	if h.auth != nil {
		if h.httpAuth(request) == false {
			request.Response.SetStatusCode(stdlibHttp.StatusProxyAuthRequired)
			request.Response.AddHeader("Proxy-Authenticate", fmt.Sprintf("Basic realm=%s", request.Host))
			return h.responseEncode.Encode(request.Response), nil
		}
	}

	// 没有指定端口, 默认使用80
	serverAddr := request.Host
	if split := strings.Split(request.Host, ":"); len(split) == 1 {
		serverAddr = fmt.Sprintf("%s:%d", split[0], 80)
	}

	if starterClient, err := h.client.DialTcp(serverAddr); err != nil {
		return nil, err
	} else {
		return starterClient.Request(session)
	}
}

func (h *HttpProxy) httpAuth(request *http.Request) bool {
	authBase := request.Header.Get("Proxy-Authorization")
	if authBase == "" {
		// 缺少认证信息
		slog.Warn("HTTP authentication failed, Lack of authentication information, proxy " + request.Host)
		return false
	}
	_, secret, found := strings.Cut(authBase, "Basic ")
	if found == false || len(secret) == 0 {
		// 认证信息格式错误
		slog.Warn(fmt.Sprintf("HTTP authentication failed, Authentication format error: %s, proxy %s", authBase, request.Host))
		return false
	}
	if decodeString, err := base64.StdEncoding.DecodeString(strings.TrimSpace(secret)); err != nil {
		// 解码认证信息失败
		slog.Warn(fmt.Sprintf("HTTP authentication failed, Base64 decoding failed: %s, proxy %s ", err, request.Host))
		return false
	} else if username, password, ok := strings.Cut(string(decodeString), ":"); !ok {
		// 解析用户和密码失败
		slog.Warn(fmt.Sprintf("HTTP authentication failed, Authentication format error: %s, proxy %s", decodeString, request.Host))
		return false
	} else {
		if h.auth.Challenge(username, password) {
			return true
		}
		slog.Warn(fmt.Sprintf("[%s] verification failed %s", username, request.Host))
		return false
	}
}

func (h *HttpProxy) httpACL(request *http.Request) bool {
	if split := strings.Split(request.Host, ":"); len(split) == 1 {
		return h.interceptor.Intercept(request.Host)
	} else {
		return h.interceptor.Intercept(split[0])
	}
}

type HttpBackend struct {
	requestEncode  *http.RequestEncode
	responseEncode *http.ResponseEncode
}

func NewHttpBackend() *HttpBackend {
	return &HttpBackend{requestEncode: http.NewRequestEncode(), responseEncode: http.NewResponseEncode()}
}

func (h *HttpBackend) Active(backendSession *event.Session, value any) (any, error) {
	proxySession, ok := value.(*event.Session)
	if !ok {
		return nil, errors.New("invalid session: " + fmt.Sprintln(proxySession))
	}
	request, ok := proxySession.GetAttr("request").(*http.Request)
	if !ok {
		return nil, errors.New("invalid request: " + fmt.Sprint(proxySession.GetAttr("request")))
	}

	slog.Info(fmt.Sprintf("http %s -> %s[%s]", proxySession.Socket.RemoteAddr().String(), request.Host, backendSession.Socket.RemoteAddr().String()))
	if request.Method == "CONNECT" {
		// 建立隧道,通知客户端http隧道建立成功
		request.Response.SetStatusCodeAndText(stdlibHttp.StatusOK, "Connection established")
		if err := h.responseEncode.Encode(request.Response); err != nil {
			return nil, err
		}
	} else {
		// 非建立隧道,直接转发请求到目标服务器
		request.BufWriter = codec.NewWriteByteBuf(backendSession.Socket.Writer)
		if err := h.requestEncode.Encoder(request); err != nil {
			return nil, err
		}
	}
	go func() {
		_, _ = codec.NewReadByteBuf(backendSession.Socket.Reader).WriteTo(proxySession.Socket)
	}()
	return codec.NewReadByteBuf(proxySession.Socket.Reader).WriteTo(backendSession.Socket)
}
